package zcatt.examples.jface.snippets;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ISynchronizable;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.reconciler.DirtyRegion;
import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
import org.eclipse.jface.text.reconciler.MonoReconciler;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModel;
import org.eclipse.jface.text.source.AnnotationRulerColumn;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationAccessExtension;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.IAnnotationPresentation;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.LineNumberChangeRulerColumn;
import org.eclipse.jface.text.source.LineNumberRulerColumn;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

/*
	
 */
public class Snippet014CompositeRuler {

	public static void main(String[] args) {
		Display display = new Display();
		Shell shell = new Shell(display);
		
		shell.setLayout(new FillLayout());
		shell.setText("Snippet014CompositeRuler");
		shell.setSize(400, 300);
		
		createSourceViewer(shell);
		
	
		shell.open();
		while(!shell.isDisposed())
		{
			if(!display.readAndDispatch())
				display.sleep();
		}
		
		shell.dispose();
	}
	
	static SourceViewer sourceViewer;
	static void createSourceViewer(Composite parent)
	{
		CompositeRuler compositeRuler = createCompositeRuler();
		sourceViewer = new SourceViewer(parent, compositeRuler, SWT.BORDER);
		sourceViewer.setDocument(new Document(
				"line1, \n"
				+"line2, hello, the world\n"
				+"line3, good morning.\n"
				+"line4, hi!\n"
				+"line5, \n"
				+"line6, verticalRuler1\n"
				+"line7, snippet013\n"
				+"line8, example.\n"
				+"line9, \n"
				+"line10, eclipse\n"
				+"line11, sourceViewer\n"
				), new AnnotationModel());
		
		MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() {

			//document changed, so update
			@Override
			public void setDocument(IDocument document) {	
				System.err.println("setDocument"+document.get());
				Map<Annotation, Position> annPosMap = getAnnotationPositionMap(sourceViewer);
				updateAnnotations(sourceViewer, annPosMap);
			}

			
			@Override	//不会被执行到.
			public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
				// skip. 
				System.err.println("reconcile");
			}

			//dirty occurs, so update
			@Override
			public void reconcile(IRegion partition) {
				//System.err.println("partition="+partition.getOffset());
				Map<Annotation, Position> annPosMap = getAnnotationPositionMap(sourceViewer);
				updateAnnotations(sourceViewer, annPosMap);
			}
			
		}, false);		//false导致调用第二个reconcile()
		
		reconciler.setDelay(1);
		reconciler.install(sourceViewer);			
	}
	
	static final int AnnotationRulerWidth = 16;
	
	
	static CompositeRuler createCompositeRuler()
	{
		CompositeRuler cp = new CompositeRuler();
		
		AnnotationRulerColumn annRulerColumn = new AnnotationRulerColumn(AnnotationRulerWidth, new MyAnnotationAccess());
		annRulerColumn.addAnnotationType(EvenLineAnnotation.Type);		//zf, 支持该类型的ann
		
		cp.addDecorator(0, annRulerColumn);
		
		LineNumberRulerColumn lineNumberRulerColumn = new LineNumberRulerColumn();
//		LineNumberChangeRulerColumn lineNumberRulerColumn = new LineNumberChangeRulerColumn(new ISharedTextColors() {
//
//			@Override
//			public Color getColor(RGB rgb) {
//				// TODO Auto-generated method stub
//				return new Color(255, 150, 150);
//			}			
//		});
		lineNumberRulerColumn.setBackground(new Color(200,255,255));
		cp.addDecorator(1,  lineNumberRulerColumn);
		
		return cp;
	}
	
	//用anns更新sourceViewer的annModel
	public static void updateAnnotations(SourceViewer viewer, Map<? extends Annotation, ? extends Position> anns)
	{
		//此即AnnotationModel
		IAnnotationModel annModel = viewer.getAnnotationModel();
		IAnnotationModelExtension annModelExt = (IAnnotationModelExtension) annModel;
		
		synchronized (getLockObject(annModel)) 
		{		
			//此处仅示意,更好的实现应当是仅删除不用和改变的anns, 再添加变更的和新增的 
			try {
				annModelExt.removeAllAnnotations();
				annModelExt.replaceAnnotations(null,  anns);				
			}
			catch(ClassCastException e)
			{
				e.printStackTrace();
			}			
		}	
		
	}

	static Object getLockObject(IAnnotationModel annotationModel) {
		if (annotationModel instanceof ISynchronizable) {
			Object lock= ((ISynchronizable) annotationModel).getLockObject();
			if (lock != null)
				return lock;
		}
		return annotationModel;
	}
	
	
	//创建偶数行指示的annotations
	public static Map<Annotation, Position> getAnnotationPositionMap(SourceViewer viewer)
	{
		Map<Annotation, Position> map = new HashMap<Annotation,Position>();
		
		IDocument doc = viewer.getDocument();
		int lineCount = doc.getNumberOfLines();
		
		for(int i = 1; i< lineCount; i+=2)
		{
			try {
				//若文档是以'\n结尾, 且取的是最后的空行, 则下面的pos会是无效pos, 
				//用于annModelExt.replaceAnnotations()会因BadLocationException而被干扰.
				if(i == lineCount -1 && doc.getLineLength(i) ==0)
					break;

				int offset= doc.getLineOffset(i);
				Position pos = new Position(offset, 1);
//				System.out.println("line="+i+", offset="+pos.offset);
				EvenLineAnnotation ann = new EvenLineAnnotation(i);
				map.put(ann, pos);
			}
			catch(BadLocationException e)
			{
//				e.printStackTrace();
			}
		}
		
//		System.out.println("anns count="+map.size());
		return map;
	}
	
	//IAnnotationPresentation用于想verticalRuler表明this annotation使用自己paint()绘制.
	public static class EvenLineAnnotation extends Annotation implements IAnnotationPresentation
	{
		public static String Type ="EvenLineAnnType"; 
		public int line;
		public EvenLineAnnotation(int line)
		{
			super(Type, false, String.valueOf(line));
			this.line = line;
		}
		
		@Override
		public int getLayer() {
			return 0;
		}
		
		@Override
		public void paint(GC gc, Canvas canvas, Rectangle bounds) {
//			System.out.print("paint "+line+", ");
			gc.setBackground(new Color(255,255, 0));
			gc.fillRectangle(bounds);
			gc.drawText("-", bounds.x, bounds.y);
		}
	}
	
	
	public static class MyAnnotationAccess implements IAnnotationAccess, IAnnotationAccessExtension
	{

		@Override
		public String getTypeLabel(Annotation annotation) {
			return "MyAnnotationAccess";
		}

		@Override
		public int getLayer(Annotation annotation) {

			//优先使用IAnnotationPresentation.getLayer()
			if (annotation instanceof IAnnotationPresentation) {
				IAnnotationPresentation presentation= (IAnnotationPresentation) annotation;
				
				return presentation.getLayer();
			}
			
			//reflection
			try {
				Method method= annotation.getClass().getMethod("getLayer"); 
				Integer result= (Integer) method.invoke(annotation);
				
				return result.intValue();
			} catch (SecurityException x) {
			} catch (IllegalArgumentException x) {
			} catch (NoSuchMethodException x) {
			} catch (IllegalAccessException x) {
			} catch (InvocationTargetException x) {
			}

			return IAnnotationAccessExtension.DEFAULT_LAYER;			
		}

		@Override
		public void paint(Annotation annotation, GC gc, Canvas canvas, Rectangle bounds) {
			//优先使用IAnnotationPresentation.getLayer()
			if (annotation instanceof IAnnotationPresentation) {
				IAnnotationPresentation presentation= (IAnnotationPresentation) annotation;
				presentation.paint(gc, canvas, bounds);		//zf, 转到annotationPresentation.paint()
				return;
			}

			//使用reflection, 直接调用annotation obj的paint(GC, Canvas, Rectangle)
			try {
				Method method= annotation.getClass().getMethod("paint", GC.class, Canvas.class, Rectangle.class);
				method.invoke(annotation, gc, canvas, bounds);			
			} catch (SecurityException x) {
			} catch (IllegalArgumentException x) {
			} catch (NoSuchMethodException x) {
			} catch (IllegalAccessException x) {
			} catch (InvocationTargetException x) {
			}
			
		}

		@Override
		public boolean isPaintable(Annotation annotation) {
			if (annotation instanceof IAnnotationPresentation)
				return true;

			boolean res = false;
			try {
				@SuppressWarnings("unused")
				Method method= annotation.getClass().getMethod("paint", GC.class, Canvas.class, Rectangle.class);
				res = true;
			} catch (SecurityException x) {
			} catch (IllegalArgumentException x) {
			} catch (NoSuchMethodException x) {
			}
			
			return res;
		}

		@Override		//zf, 若this annAccess支持访问/绘制该类型的ann, 则返回true
		public boolean isSubtype(Object annotationType, Object potentialSupertype) {	//zf, annotationType是evenLineAnnotation.getType()的返回值
			if(EvenLineAnnotation.Type == (String)annotationType)
			{
				return true;
			}
			return false;
		}

		@Override
		public Object[] getSupertypes(Object annotationType) {
			return null;
		}

		@Override
		public Object getType(Annotation annotation) {
			return annotation.getType();
		}

		@Override
		public boolean isMultiLine(Annotation annotation) {
			return true;
		}

		@Override
		public boolean isTemporary(Annotation annotation) {
			return !annotation.isPersistent();
		}
		
	}
	

}
